package net.esj.basic.utils.writer.json;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import net.esj.basic.annotation.IgnorePrint;
import net.esj.basic.utils.DateFormat;
import net.esj.basic.utils.view.JSONStringObject;
import net.esj.basic.utils.writer.AbstractWriter;
import net.esj.basic.utils.writer.Signed;
import net.esj.basic.utils.writer.WriteException;
import net.esj.basic.utils.writer.WriterCode;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONString;

public class JsonWriter extends AbstractWriter{

	@Override
	public String write(Object object) throws WriteException{
		return write(object, null,null);
	}

	@Override
	public String write(Object object, WriterCode code) throws WriteException {
		return write(object,null,code);
	}
	
	@Override
	public String write(Object object, Signed signed) throws WriteException{
		return write(object, signed, WriterCode.NULL);
	}
	
	@Override
	public String write(Object object, Signed signed, WriterCode code)
			throws WriteException {
		try {
			return getJSONObject(object, signed,code,new HashSet<Integer>()).toString();
		} catch (Exception e) {
			throw new WriteException(e);
		} 
	}

	protected JSONStringObject reflectObject(Object bean,Signed signed,WriterCode code,Set<Integer> deadlockSet) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, JSONException {
		JSONObject jSONObject = new JSONObject();
		/*if(deadlockSet.contains(bean.hashCode())){//通过hashcode防止进入死循环，TODO in test
			return new JSONStringObject("");
		}
		deadlockSet.add(bean.hashCode());*/

        Class klass = bean.getClass();
        	
        Map<String,Method> map = new HashMap<String, Method>();
        resolveClass(klass, signed, map);
        
        for(Map.Entry<String, Method> entry:map.entrySet()){
        	if(entry.getValue()==null ){
        		continue;
        	}
        	if("class".equals(entry.getKey())){//忽略getClass方法
        		continue;
        	}
        	
        	//Signed sig = pd.getPropertyType().getAnnotation(Signed.class);
        		//处理
    		Method readMethod = entry.getValue();
			if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
				readMethod.setAccessible(true);
			}
    		Object value = readMethod.invoke(bean);
			jSONObject.put(proxyJsonObjectName(entry.getKey(),value,signed,code), getJSONObject(value,signed,code,deadlockSet));
        }
        return new JSONStringObject(jSONObject.toString());
    }
	
	public JSONStringObject getJSONObject(Object value, Signed signed,WriterCode code,Set<Integer> deadlockSet) throws JSONException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{

        //处理原始类型
        value = proxyCheck(value);
        if (value == null) {
            return new JSONStringObject("null");
        }
        if (value instanceof JSONString) {
            Object o;
            try {
                o = ((JSONString)value).toJSONString();
            } catch (Exception e) {
                throw new JSONException(e);
            }
            throw new JSONException("Bad value from toJSONString: " + o);
        }
        if (value instanceof Number){
            return new JSONStringObject(JSONObject.numberToString((Number) value));
        }
        if ( value instanceof JSONObject ||
                value instanceof JSONArray) {
            return new JSONStringObject(value.toString());
        }
        if (value instanceof String)
            return new JSONStringObject(JSONObject.quote(value.toString()));
        if (value instanceof Map) {
           
            JSONObject jSONObject = new JSONObject();

            Iterator iterator = ((Map)value).keySet().iterator();
            while(iterator.hasNext()){
                String key = iterator.next().toString();
                Object valueObj = ((Map)value).get(key);
                jSONObject.put(key, getJSONObject(valueObj,signed,code,deadlockSet));
            }
            return new JSONStringObject(jSONObject.toString());
        }

        //class
        if(value instanceof Class)
            return new JSONStringObject(JSONObject.quote(((Class)value).getSimpleName()));
       
        //数组
        if (value instanceof Collection || value.getClass().isArray()) {
            return new JSONStringObject(getJSONArray(proxyCheck(value), signed,code,deadlockSet));
        }

        if(value instanceof Date){
        	  return new JSONStringObject("\""+DateFormat.formateDateTime((Date)value, DateFormat.DEFAULT_DATETIME_FORMAT_SEC)+"\"");
        }
        
        if(value instanceof Boolean){
        	if(!(Boolean)value){
        		return new JSONStringObject("false");
        	}else{
        		return new JSONStringObject("true");
        	}
        }
        
        return reflectObject(value,signed,code,deadlockSet);
    }
	
	private String getJSONArray(Object arrayObj, Signed signed,WriterCode code,Set<Integer> deadlockSet) throws JSONException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
	       
        if(arrayObj == null)
            return "null";
       
        arrayObj = proxyCheck(arrayObj);
       
        JSONArray jSONArray = new JSONArray();
        if(arrayObj instanceof Collection){
            Iterator iterator = ((Collection)arrayObj).iterator();
            while(iterator.hasNext()){
                Object rowObj = iterator.next();
                if(rowObj == null)
//                    jSONArray.put(new JSONStringObject(null));
                	jSONArray.put(new JSONStringObject("null"));
                else if(rowObj.getClass().isArray() || rowObj instanceof Collection)
                    jSONArray.put(getJSONArray(rowObj, signed,code,deadlockSet));
                else
                    jSONArray.put(getJSONObject(rowObj,signed,code,deadlockSet));
            }
        }
        if(arrayObj.getClass().isArray()){
            int arrayLength = Array.getLength(arrayObj);
            for(int i = 0; i < arrayLength; i ++){
                Object rowObj = Array.get(arrayObj, i);
                if(rowObj == null)
                    //jSONArray.put(new JSONStringObject(null));
                	jSONArray.put(new JSONStringObject("null"));
                else if(rowObj.getClass().isArray() || rowObj instanceof Collection)
                    jSONArray.put(getJSONArray(rowObj, signed,code,deadlockSet));
                else
                    jSONArray.put(getJSONObject(rowObj,signed,code,deadlockSet));
            }
        }
        return jSONArray.toString();
    }
	
	protected Object proxyCheck(Object value){
		return value;
	}

	protected String proxyJsonObjectName(String name,Object target,Signed signed,WriterCode code){
		return name;
	}
	
	private void resolveClass(Class klass,Signed signed,Map<String,Method> map){
		Method[] methods = klass.getMethods();
        for (int i = 0; i < methods.length; i += 1) {
            try {
                Method method = methods[i];
                method.setAccessible(true);
                String name = method.getName();
                String key = "";
                if(method.isAnnotationPresent(IgnorePrint.class)){
                	continue;
                }
                if (name.startsWith("get")) {
                    key = name.substring(3);
                } else if (name.startsWith("is")) {
                    key = name.substring(2);
                } else{
                	continue;
                }
                Signed sig = method.getAnnotation(Signed.class);
            	boolean can = canWrite(sig, signed);
            	if(!can || "class".equals(key)){
            		continue;
            	}
                if (key.length() > 0 &&
                        Character.isUpperCase(key.charAt(0)) &&
                        method.getParameterTypes().length == 0) {
                    if (key.length() == 1) {
                        key = key.toLowerCase();
                    } else if (!Character.isUpperCase(key.charAt(1))) {
                        key = key.substring(0, 1).toLowerCase() +
                            key.substring(1);
                    }
                    map.put(key, method);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        for(Class intf:klass.getInterfaces()){
        	resolveClass(intf, signed, map);
        }
        if(klass.getSuperclass()!=null && klass.getSuperclass()!=Object.class){
        	resolveClass(klass.getSuperclass(), signed, map);
        }
	}
}
